﻿using System;
using System.Windows;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Bling.DSL;
using Bling.WPF;
using Bling.Util;
using Bling.Physics;
using Bling.Animation;

namespace Bling.PhysicsTutorial {
  internal static class ParticleTestExtensions {

  }

  public class Fireworks : PhysicsCanvas {
    public class CustomBlock : PhysicsBlock<Ellipse, EllipseBl, CustomBlock>,
      ParticleSystem.Is<CustomBlock>,
            RigidBody.Is<CustomBlock>,
ParticleSystemUI.Is<Ellipse, CustomBlock> {
      public readonly Random r = new Random();
      public readonly CanvasBl Owner;
      public readonly SweepList<int> Collisions;


      public DoubleBl Radius(IntBl idx) {
        return 25d * this[idx].Scale();
      }
      public DoubleBl End(IntBl idx) {
        return this[idx].Center().X + Radius(idx) * 2d;
      }

      public CustomBlock(CanvasBl Owner, World World, int Count)
        : base(World, Count, idx => {
          return new EllipseBl(Owner) {
            Size = new PointBl(50,50), Fill = idx.SelectColor(), 
          };
        }) {
        new ParticleSystem.Impl<CustomBlock>(this) {
          EmitNew = (block) => block.EmitNew(),
          InitEmit = (block) => block.InitEmit(),
        };
        new RigidBody.Impl<CustomBlock>(this);
        new ParticleSystemUI.Impl<Ellipse, CustomBlock>(this);
        this.Owner = Owner;
        Collisions = new SweepList<int>(World);
      }
      protected override void Init() {
        base.Init();
        ForAll(body => {
          body.Center().Step = (nowV, oldV) => nowV.VerletStep(.02, oldV);
          var sz = new PointBl(25, 25) * body.Scale();

          body.Center().Old = body.Center().Old.Restitution(body.Center(), sz, Owner.Size - sz);
          body.Center().Bind = body.Center().Clamp(sz, Owner.Size - sz);
          body.Center().Bind += new PointBl(0, 1) * body.Scale().Pow(2);


          body.Scale().Bind = (body.Scale() + .01).Min(body.AgeRatio());
          //body.AgeRatio().Link.To = body.At.Opacity;
        });
      }
      protected override void Activate() {
        base.Activate();
        Collisions.Compare = DoubleBl.Func<int, int>((i0, i1) => {
          return this[i0].Center().X - this[i1].Center().X;
        });
        Collisions.CompareEnd = DoubleBl.Func<int, int>((i0, i1) => {
          return End(i0) - End(i1);
        });
        Collisions.Active = BoolBl.Func<int>(i => {
          return this[i].AgeRatio() > 0;
        });
        Collisions.CanCollide = BoolBl.Func<int, int>((i0,i1) => {
          // radius is 25.
          return End(i0) > this[i1].Center().X;
          //var diff = this[i1].Position().X - this[i0].Position().X;
          //return diff.Abs < 50d;
        });
        var CheckY = BoolBl.Func<int, int>((i0, i1) => {
          //var diff = this[i1].Center().Y - this[i0].Center().Y;
          //return diff.Abs <= 8d;
         // return diff.Abs < this[i1].Radius() * 2d | diff.Abs < this[i0].Radius() * 2d;

          var diff = this[i1].Center().Y - this[i0].Center().Y;
          diff = diff.Abs;
          diff = diff / 2d;
          return diff < Radius(i1) | diff < Radius(i0) ;
        });
        var ComputeVec = PointBl.Func<int, int>((i0, i1) => {
          var diff = this[i1].Center() - this[i0].Center();
          var l = diff.Length;
          var diff0 = (l == 0).Condition(new PointBl(0,1), new PointBl(diff.X / l, diff.Y / l));
          //diff0 = diff0.Check("NaN: 1=".Bl() + diff + " 0=" + diff0 + " L=" + l + " X=" + (diff.X / l) + " Y=" + (diff.Y / l));
          var total = Radius(i0) + Radius(i1);
          l = (total - l).Max(0);
          return diff0 * l * .5;
        });
        var Correct = PointBl.Assign<int, Point>((i, p) => this[i].Center(), (i, p) => this[i].Center() + p.Bl());
        var Chop = DoubleBl.Assign<int,Point>((i,p) => this[i].Scale(), (i,p) => {
          return this[i].Scale() * (p.Bl().X == 0 & p.Bl().Y == 0).Condition(1, .99);
        });
        Collisions.DoCollide = (i0, i1) => {
          if (CheckY(i0, i1)) {
            var diff = ComputeVec(i0, i1);
            Chop(i1,diff);
            Chop(i0,diff);
            Correct(i1, diff);
            Correct(i0, new Point(-diff.X, -diff.Y));

          }
          return;
        };
      }
      public Action<int> InitEmit() {
        Action<int,double> SetAge = this.SetAge();
        DoubleBl dr = r;
        Action<int> SetPositionA = PointBl.Assign<int>(i => this[i].Center(), i => new PointBl(Owner.Width / 2d, Owner.Height / 2d));
        Action<int> SetPositionB = PointBl.Assign<int>(i => this[i].Center().Old, i => new PointBl(this[i].Center().X - 30d + 60d * dr, Owner.Height / 2d + 30d * dr));
        Action<int> SetScale = DoubleBl.Assign<int>(i => this[i].Scale(), i => .01 + .99 * dr);
        return idx => {
          SetAge(idx, r.NextDouble() * 10000);
          SetPositionA(idx);
          SetPositionB(idx);
          SetScale(idx);
          Collisions.Insert(idx);
        };
      }
      public double EmitNew() {
        return Emit0();
      }
      public Func<double> Emit0 = () => 1d;
    }
    static Fireworks() {
    }

    public Fireworks() : base("Fireworks", 10) {
      Background = Brushes.LightGray;
    }
    protected override Action Init(CanvasBl MainCanvas, World World) {
      var block = new CustomBlock(MainCanvas, World, 500);
      var slider = new LabelSliderBl(MainCanvas) {
        Width = MainCanvas.Width, Minimum = 0, Maximum = 10, Value = 2,
        ZIndex = 10, RightBottom = MainCanvas.Size, LabelName = "Spawn",
      };
      block.Emit0 = slider.Value.AsFunc;

      return () => { };
    }
  }

}
